package org.ipccenter.balancechecker.security;
import java.io.*;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;


public class JavaSecurity {

    /**
     * Reads serialized SecretKeySpec form file
     * @param fileName The name of the file from which to read a key. If it's null then file named "data" will be chosen.	 *
     */

    public void readPrivateKey(String fileName) {
        String name;
        if (fileName == null)
            name = "data";
        else
            name = fileName;
        ObjectInputStream keyIn = null;
        SecretKeySpec key = null;
        try {
            keyIn = new ObjectInputStream(new FileInputStream(name));
            key = (SecretKeySpec) keyIn.readObject();
            keyIn.close();
        } catch(IOException e) {
            System.out.println("An error occured while reading private key\n" + e.getStackTrace());
        } catch(ClassNotFoundException e1) {
            System.out.println("Key object can't be found in file " + name + e1.getStackTrace());
        }
        this.key = key;
        this.initiated = true;
    }

    /**
     * Initialize object with user defined password. Then methods encrypt and decrypt are available.
     * @param userPassword Password to use
     */

    public void init(String userPassword) {
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");
            SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
            cipher.init(Cipher.ENCRYPT_MODE ,keySpec);
        } catch( Exception e ) {
            e.printStackTrace();
        }
        byte[] encryptedUserPassword = this.crypt(userPassword.getBytes(),cipher);
        key = new SecretKeySpec(encryptedUserPassword, "AES");
        this.initiated = true;
    }

    /**
     *Serializes SecretKeySpec to file
     * @param fileName The name of the file to write a key. If it's null then file named "data" will be chosen.
     */

    public void writePrivateKey(String fileName) {
        String name;
        if (fileName == null)
            name = "data";
        else
            name = fileName;
        ObjectOutputStream keyOut = null;
        try {
            keyOut = new ObjectOutputStream(new FileOutputStream(name));
            keyOut.writeObject(this.key);
            keyOut.close();
        } catch(IOException e) {
            System.out.println("An error occured while writing private key\n" + e.getStackTrace());
        }
    }

    /**
     * Encrypts String to another string.
     * @param pass String to encrypt
     * @return Encrypted String. Null if class has not been initialized.
     */

    public String encrypt(String pass) {

        if(this.initiated == false)
            return null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE ,key);
        } catch( Exception e ) {
            e.printStackTrace();
        }

        return Base64.encodeBase64String(this.crypt(pass.getBytes(),cipher));
    }

    /**
     * Decrypts String to initial String.
     * @param pass String to decrypt
     * @return Decrypted String. Null if class has not been initialized.
     */

    public String decrypt(String pass) {

        if(this.initiated == false)
            return null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE ,key);
        } catch( Exception e ) {
            e.printStackTrace();
        }
        byte[] passBytes = Base64.decodeBase64(pass);
        return new String(this.crypt(passBytes,cipher));
    }

    private byte[] crypt(byte[] inBytes, Cipher cipher) {
        int blockSize = cipher.getBlockSize();
        int outputSize = cipher.getOutputSize(blockSize);

        byte[] tempBytes = new byte[blockSize];
        byte[] outBytes = new byte[outputSize];
        ByteArrayInputStream in = new ByteArrayInputStream(inBytes);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            int inLength = 0;
            boolean more = true;
            while (more) {
                inLength = in.read(tempBytes);
                if (inLength == blockSize)
                {
                    int outLength = cipher.update(inBytes, 0, blockSize, outBytes, 0);
                    out.write(outBytes, 0, outLength);
                }
                else more = false;
            }

            if (inLength > 0)
                outBytes = cipher.doFinal(inBytes, 0, inLength);
            else
                outBytes = cipher.doFinal();
            out.write(outBytes);
        } catch(Exception e ) {
            e.printStackTrace();
        }
        return out.toByteArray();
    }

    private byte[] keyByte = new byte[] { 'D', 'a', 't', 'a', 'F', 'o', 'r',
            'K', 'e', 'y', '0','0', '0', '0', '0', '0' };
    private boolean initiated = false;
    private SecretKeySpec key;
    /**
     * @param args
     */
    public static void main(String[] args) {
        JavaSecurity tester = new JavaSecurity();
        tester.init("123123aA");
        //tester.readPrivateKey(null);
        try{
            String s= tester.encrypt("123123");
            System.out.println(s);
            System.out.println(tester.decrypt(s));
        } catch(Exception e ) {
            e.printStackTrace();
        }
    }

}

